﻿using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace NetPay.WeChat.ProfitSharing
{
    /// <summary>
    /// 签名认证-HTTP头参数
    /// </summary>
	public class Authorization
	{
        /// <summary>
        /// 获取签名认证-GET
        /// </summary>
        /// <param name="url">请求URL</param>
        /// <param name="method">请求方法</param>
        /// <param name="mchid">商户ID</param>
        /// <param name="serial_no">商户私钥证书对应的序列号</param>
        /// <returns></returns>
        public static string GetAuthorization(string url, string method, string mchid, string serial_no)
        {
            var uri = new Uri(url);
            string url_path = uri.PathAndQuery;
            string nonce = GetRandomString(32);
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            method = string.IsNullOrEmpty(method) ? "" : method;
            // ！！！特别注意此处：GET请求后面有两个\n
            string message = string.Format("{0}\n{1}\n{2}\n{3}\n\n", method, url_path, timestamp, nonce);

            string sign = Sign(message);

            // 签名认证格式
            string authorization = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"",
                mchid,
                nonce,
                timestamp,
                serial_no,
                sign
                );

            return authorization;
        }

        /// <summary>
        /// 获取签名认证-POST
        /// </summary>
        /// <param name="url">请求URL</param>
        /// <param name="method">请求方法</param>
        /// <param name="data">数据对象</param>
        /// <param name="mchid">商户ID</param>
        /// <param name="serial_no">商户私钥证书对应的序列号</param>
        /// <returns></returns>
        public static string GetAuthorization(string url, string method, string data, string mchid, string serial_no)
        {
            var uri = new Uri(url);
            string url_path = uri.PathAndQuery;
            string nonce = GetRandomString(32);
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            method = string.IsNullOrEmpty(method) ? "" : method;
            var message = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", method, url_path, timestamp, nonce, data);

            string sign = Sign(message);

            // 签名认证格式
            string authorization = string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"",
                mchid,
                nonce,
                timestamp,
                serial_no,
                sign
                );

            return authorization;
        }

        /// <summary>
        /// 签名
        /// </summary>
        /// <param name="message">需要签名的消息体</param>
        /// <returns></returns>
        public static string Sign(string message)
        {
            // 从证书中读取私钥
            var path = System.Environment.CurrentDirectory + "/wwwroot/apiclient_cert.p12";

            X509Certificate2 cert = new X509Certificate2(path, Config.MCHID, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
            var private_key = cert.PrivateKey.ToXmlString(true);

            using (RSACryptoServiceProvider sha256 = new RSACryptoServiceProvider())
            {
                byte[] dataInBytes = Encoding.UTF8.GetBytes(message);
                sha256.FromXmlString(private_key);
                byte[] inArray = sha256.SignData(dataInBytes, CryptoConfig.MapNameToOID("SHA256"));
                string sign = Convert.ToBase64String(inArray);

                return sign;
            }
        }

        /// <summary>
        /// 加密微信支付平台证书序列号
        /// </summary>
        /// <param name="text">微信支付平台证书序列号</param>
        /// <param name="publicKey">公钥</param>
        /// <returns></returns>
        public static string RSAEncrypt(string text, byte[] publicKey)
        {
            var cer = new X509Certificate2(publicKey);
            var rsaParam = cer.GetRSAPublicKey().ExportParameters(false);
            var rsa = new RSACryptoServiceProvider();
            rsa.ImportParameters(rsaParam);
            var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), true);

            return Convert.ToBase64String(buff);
        }

        /// <summary>
        /// 生成随机字符串
        /// </summary>
        /// <param name="length">字符串长度</param>
        /// <returns></returns>
        public static string GetRandomString(int length)
        {
            const string key = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";

            Random rnd = new Random();
            byte[] buffer = new byte[8];

            ulong bit = 31;
            ulong result = 0;
            int index = 0;
            StringBuilder sb = new StringBuilder((length / 5 + 1) * 5);

            while (sb.Length < length)
            {
                rnd.NextBytes(buffer);

                buffer[5] = buffer[6] = buffer[7] = 0x00;
                result = BitConverter.ToUInt64(buffer, 0);

                while (result > 0 && sb.Length < length)
                {
                    index = (int)(bit & result);
                    sb.Append(key[index]);
                    result = result >> 5;
                }
            }

            return sb.ToString();
        }
    }
}

